package contegor.scire.core.queries;

import com.sun.corba.se.spi.ior.Identifiable;

import contegor.scire.core.Connectable;
import contegor.scire.core.Id;
import contegor.scire.core.Link;
import contegor.scire.core.Version;

/**
 * This {@link Condition} filters {@link Connectable}s and let them pass if they are connected with an
 * {@link Identifiable} identified with the same {@link Id} as the one given to this.
 * Also allows nesting other {@link Condition} that are applied to the {@link Link} between
 * the {@link Connectable} and the {@link Identifiable} if such {@link Link} exists.
 *
 * @version $Revision$
 * @author  Héctor Cirbián Pousa ($Author$)
 */
@Version("$Id$")
public class IsConnectedWith<C extends Connectable> extends WeightCondition<C> {
  private static final long serialVersionUID = -2238761609018911511L;

  private Id              target;
  private Condition<Link> nested;

  /**
   * Creates a {@link Condition} that filters {@link Connectable}s and lets them pass if they are source of a {@link Link} which's
   * target is the one who's {@link Id} is given.
   */
  public IsConnectedWith(Id target) {
    super();
    this.target = target;
    this.nested = null;
  }

  /**
   * Creates a {@link Condition} that filters {@link Connectable}s and lets them pass if they are source of a {@link Link} which's
   * target is the one who's {@link Id} is given. The {@link Link} in between must also fulfill with the nested {@link Condition}.
   * The {@link Condition#eval(Connectable)} method will do a logic <code>and</code> between the two checks. It is granted that the main condition will
   * be evaluated first and, if it is true, the nested will be evaluated.
   */
  public IsConnectedWith(Id target, Condition<Link> nested) {
    super();
    this.target = target;
    this.nested = nested;
  }

  @Override
  public boolean eval(C source) {
    if (source.isLinkedWith(target)) {
      if (nested == null)
        return true;
      return nested.eval(source.getLink(target));
    }
    else
      return false;
  }

  @Override
  public int weight(C source) {
    int result = 0;
    if (source.isLinkedWith(target)) {
      result++;
      if (nested != null) {
        if (nested instanceof WeightCondition<?>)
          result += ((WeightCondition<Link>)nested).weight(source.getLink(target));
        else
          result += nested.eval(source.getLink(target)) ? 1 : 0;
      }
    }
    return result;
  }

  @Override
  public IsConnectedWith<C> clone() {
    // Id is immutable.
    if (this.nested == null)
      return new IsConnectedWith<C>(this.target);
    else
      return new IsConnectedWith<C>(this.target, this.nested.clone());
  }
}
